package distrcomp;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.rmi.RemoteException;
import java.rmi.server.UnicastRemoteObject;
import java.util.HashMap;

import javax.rmi.ssl.SslRMIClientSocketFactory;
import javax.rmi.ssl.SslRMIServerSocketFactory;

public class SubserverImpl implements Subserver, Serializable {
	private static final long serialVersionUID = 1L; // Serial version ID
	private int ID;
	private HashMap<Integer, Shipment> shipments;
	private HashMap<ShippingType, Float> prices;
	
	//Constructor
	public SubserverImpl() throws RemoteException {
		this.shipments = new HashMap<Integer, Shipment>();
		this.prices = new HashMap<ShippingType, Float>();
		UnicastRemoteObject.exportObject(this, 0, new SslRMIClientSocketFactory(), new SslRMIServerSocketFactory(null, null, true));
	}
	
	//Constructor
	public SubserverImpl(boolean restore) throws RemoteException {
		this.shipments = new HashMap<Integer, Shipment>();
		this.prices = new HashMap<ShippingType, Float>();
		
		try {
			FileInputStream fileStream = new FileInputStream("state.dat");
		    ObjectInputStream objectStream = new ObjectInputStream(fileStream);
		    SubserverImpl sub = ((SubserverImpl) objectStream.readObject());
		    this.shipments = sub.shipments;
		    this.prices = sub.prices;
		    this.ID = sub.ID;
		    objectStream.close();
		    System.out.println("Restored succesfully.");
		} catch (Exception e) {
			System.out.println("Error restoring.");
		}
		UnicastRemoteObject.exportObject(this, 0, new SslRMIClientSocketFactory(), new SslRMIServerSocketFactory(null, null, true));
	}

	//Request a shipment. Returns a shipment when successful, otherwise returns null
	public Shipment ship(Product product, Address source, Address destination, ShippingType shippingType, int ID) throws RemoteException {
		Shipment shipment = new Shipment(source, destination, shippingType, product, prices.get(shippingType), ID);
		this.shipments.put(ID, shipment);
		ShippingSimulator simulator = new ShippingSimulator(shipment);
		simulator.start();
		return shipment;
	}
	
	//Starts the tracking (and simulation) of the shipment
	public void startTracker(ShippingTracker tracker, int shipmentID, ShippingTracker notifier) throws RemoteException {
		if(this.shipments.containsKey(shipmentID)) {
			ServerTrackerImpl sTracker = new ServerTrackerImpl(tracker, notifier, this.shipments.get(shipmentID));
			sTracker.start();
		}
	}

	public void setPrice(ShippingType type, float price) throws RemoteException {
		this.prices.put(type, price);
		System.out.println("Received shipping rate update - " + type + ": " + price);
	}
	
	public void setPrices(float ground, float air, float air_pr) throws RemoteException {
		this.prices.put(ShippingType.GROUND, ground);
		this.prices.put(ShippingType.AIR, air);
		this.prices.put(ShippingType.AIR_PRIORITY, air_pr);
		System.out.println("Received shipping rates update");
	}
	
	public Shipment getShipment(int ID) throws Exception {
		if(this.shipments.containsKey(ID)) {
			return this.shipments.get(ID);
		} else {
			throw new Exception("Shipment not found.");
		}
	}
	
	public void backup() throws RemoteException {
		try {
			FileOutputStream fileStream = new FileOutputStream("state.dat");
			ObjectOutputStream objectStream = new ObjectOutputStream(fileStream);
			objectStream.writeObject(this);
			objectStream.close();
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("Error backing up.");
		}
	}
	
	public void setID(int id) {
		this.ID = id;
	}
	
	public int getID() {
		return this.ID;
	}
}
